package com.zhouyu.fileSync.FSclient.transmission.communicationBetweenClients.server;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zhouyu.fileSync.FScommon.api.entities.MessageType;
import com.zhouyu.fileSync.FScommon.api.entities.Request;
import com.zhouyu.fileSync.FScommon.api.entities.Response;
import com.zhouyu.fileSync.FScommon.commonUtils.FileUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.DefaultFileRegion;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.AttributeKey;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.io.RandomAccessFile;
import java.lang.reflect.Method;
import java.nio.channels.WritableByteChannel;
import java.util.HashMap;

/**
 * Created by zhouyu74748585@hotmail.com on 2019/9/6
 **/
public class CommunicationServerHandler extends ChannelInboundHandlerAdapter {

    Logger logger = LogManager.getLogger(CommunicationServerHandler.class);

    private final HashMap<String, Object> serviceMap;


    public CommunicationServerHandler(HashMap<String, Object> serviceMap) {
        this.serviceMap = serviceMap;
    }



    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Request request = JSON.parseObject(msg.toString(), Request.class);
        if (request.getMessageType() == MessageType.METHOD) {
            logger.info("RPC client ask for interface:" + request.getClassName() + "  , method:" + request.getMethodName());
            Response response = null;
            RandomAccessFile raf = null;
            long length = -1;
            try {
                Object result = this.handler(request);
                if (result instanceof File){
                    //todo 获取文件md5并返回，并添加一个发送文件的任务
                   raf = new RandomAccessFile((File) result,"rw");
                    length = raf.length();
                    if (raf!=null&&length>0) {
                        ctx.writeAndFlush(Unpooled.copiedBuffer(("FILE:"+request.getRequestId()+":"+ FileUtils.getMd5((File) result)).getBytes()));
                        long pageCount=length%65400==0?length/65400:length/65400+1;
                        int count=65400;
                        for (int i = 1; i <= pageCount ; i++) {
                            if (i==pageCount){
                                count= length%65400==0?65400: (int) (length % 65400);
                            }
                            byte[] bytes=new byte[count];
                            raf.seek((i-1)*65400);
                            raf.read(bytes,0,count);
                            ctx.write(Unpooled.copiedBuffer(bytes));
                            if (i==pageCount){
                                ctx.write(Unpooled.copiedBuffer(("_FILE_END_").getBytes()));
                            }
                            ctx.writeAndFlush(Unpooled.copiedBuffer("#_@_#".getBytes()));
                        }
                    }
                }else{
                    response=new Response();
                    response.setRequestId(request.getRequestId());
                    response.setData(result);
                }
            } catch (Throwable e) {
                e.printStackTrace();
                response.setCode(1);
                response.setErrorMsg(e.toString());
                logger.error("RPC Server handle request error", e);
            }finally {
                if (length > 0 && raf != null) {
                    raf.close();
                    System.out.println("文件"+raf.getFD().toString()+" 关闭");
                }
            }
            if (response!=null) {
                ctx.write(Unpooled.copiedBuffer(("RESPONSE:"+request.getRequestId()).getBytes()));
                ctx.writeAndFlush(response);
            }
        } else if (request.getMessageType() == MessageType.HEARTBEAT) {
            logger.info(request.getParameters()[0]+"客户端发送心跳信息");
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.ALL_IDLE) {
                logger.info("no message had been received or send in the last 60s ,close the channel.{}", ctx.channel().remoteAddress());
                //clear discount client
                ctx.channel().close();
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }


    /**
     * 通过反射，执行本地方法
     *
     * @param request
     * @return
     * @throws Throwable
     */
    private Object handler(Request request) throws Throwable {
        String className = request.getClassName();
        Object serviceBean = serviceMap.get(className);

        if (serviceBean != null) {
            Class<?> serviceClass = serviceBean.getClass();
            String methodName = request.getMethodName();
            Class<?>[] parameterTypes = request.getParameterTypes();
            Object[] parameters = request.getParameters();

            Method method = serviceClass.getMethod(methodName, parameterTypes);
            method.setAccessible(true);
            return method.invoke(serviceBean, getParameters(parameterTypes, parameters));
        } else {
            throw new Exception("未找到服务接口,请检查配置!:" + className + "#" + request.getMethodName());
        }
    }

    /**
     * 获取参数列表
     *
     * @param parameterTypes
     * @param parameters
     * @return
     */
    private Object[] getParameters(Class<?>[] parameterTypes, Object[] parameters) {
        if (parameters == null || parameters.length == 0) {
            return parameters;
        } else {
            Object[] new_parameters = new Object[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                if (parameters[i] instanceof JSON){
                    new_parameters[i] = JSONObject.toJavaObject((JSON) parameters[i], parameterTypes[i]);
                }else{
                    new_parameters[i] = parameters[i];
                }
            }
            return new_parameters;
        }
    }


}
